\chapter{bsp\+\_\+can }
\hypertarget{md_bsp_2can_2bsp__can}{}\label{md_bsp_2can_2bsp__can}\index{bsp\_can@{bsp\_can}}
\label{md_bsp_2can_2bsp__can_autotoc_md0}%
\Hypertarget{md_bsp_2can_2bsp__can_autotoc_md0}%




\href{mailto:neozng1@hnu.edu.cn}{\texttt{neozng1@hnu.\+edu.\+cn}}\hypertarget{md_bsp_2can_2bsp__can_autotoc_md1}{}\doxysection{\texorpdfstring{请注意使用\+CAN设备的时候务必保证总线只接入了2个终端电阻！开发板一般都有一个，6020电机、c620/c610电调、\+LK电机也都有终端电阻，注意把多于2个的全部断开（通过拨码）}{请注意使用\+CAN设备的时候务必保证总线只接入了2个终端电阻！开发板一般都有一个，6020电机、c620/c610电调、\+LK电机也都有终端电阻，注意把多于2个的全部断开（通过拨码）}}\label{md_bsp_2can_2bsp__can_autotoc_md1}
\hypertarget{md_bsp_2can_2bsp__can_autotoc_md2}{}\doxysubsection{\texorpdfstring{使用说明}{使用说明}}\label{md_bsp_2can_2bsp__can_autotoc_md2}
若你希望新增一个基于\+CAN的module，首先在该模块下应该有一个包含{\ttfamily can\+\_\+instance}指针的module结构体（或当功能简单的时候，可以是单独存在的{\ttfamily can\+\_\+instance}，但不推荐这样做）。\hypertarget{md_bsp_2can_2bsp__can_autotoc_md3}{}\doxysubsection{\texorpdfstring{代码结构}{代码结构}}\label{md_bsp_2can_2bsp__can_autotoc_md3}
.h文件内包括了外部接口和类型定义,以及模块对应的宏。c文件内为私有函数和外部接口的定义。\hypertarget{md_bsp_2can_2bsp__can_autotoc_md4}{}\doxysubsection{\texorpdfstring{类型定义}{类型定义}}\label{md_bsp_2can_2bsp__can_autotoc_md4}

\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{preprocessor}{\#define\ MX\_REGISTER\_DEVICE\_CNT\ 12\ \ }\textcolor{comment}{//\ maximum\ number\ of\ device\ can\ be\ registered\ to\ CAN\ service,\ this\ number\ depends\ on\ the\ load\ of\ CAN\ bus.}}
\DoxyCodeLine{\textcolor{preprocessor}{\#define\ MX\_CAN\_FILTER\_CNT\ (4\ *\ 14)\ }\textcolor{comment}{//\ temporarily\ useless}}
\DoxyCodeLine{\textcolor{preprocessor}{\#define\ DEVICE\_CAN\_CNT\ 2\ \ \ \ \ \ \ \ \ \ \ }\textcolor{comment}{//\ CAN1,CAN2}}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{comment}{/*\ can\ instance\ typedef,\ every\ module\ registered\ to\ CAN\ should\ have\ this\ variable\ */}}
\DoxyCodeLine{\textcolor{keyword}{typedef}\ \textcolor{keyword}{struct\ }\mbox{\hyperlink{struct__}{\_}}}
\DoxyCodeLine{\{}
\DoxyCodeLine{\ \ \ \ CAN\_HandleTypeDef\ *can\_handle;\ \textcolor{comment}{//\ can句柄}}
\DoxyCodeLine{\ \ \ \ CAN\_TxHeaderTypeDef\ txconf;\ \ \ \ \textcolor{comment}{//\ CAN报文发送配置}}
\DoxyCodeLine{\ \ \ \ uint32\_t\ tx\_id;\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 发送id}}
\DoxyCodeLine{\ \ \ \ uint32\_t\ tx\_mailbox;\ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ CAN消息填入的邮箱号}}
\DoxyCodeLine{\ \ \ \ uint8\_t\ tx\_buff[8];\ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 发送缓存,发送消息长度可以通过CANSetDLC()设定,最大为8}}
\DoxyCodeLine{\ \ \ \ uint8\_t\ rx\_buff[8];\ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 接收缓存,最大消息长度为8}}
\DoxyCodeLine{\ \ \ \ uint32\_t\ rx\_id;\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 接收id}}
\DoxyCodeLine{\ \ \ \ uint8\_t\ rx\_len;\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 接收长度,可能为0-\/8}}
\DoxyCodeLine{\ \ \ \ \textcolor{comment}{//\ 接收的回调函数,用于解析接收到的数据}}
\DoxyCodeLine{\ \ \ \ void\ (*can\_module\_callback)(\textcolor{keyword}{struct\ }\_\ *);\ \textcolor{comment}{//\ callback\ needs\ an\ instance\ to\ tell\ among\ registered\ ones}}
\DoxyCodeLine{\ \ \ \ \textcolor{keywordtype}{void}\ *id;\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \textcolor{comment}{//\ 使用can外设的模块指针(即id指向的模块拥有此can实例,是父子关系)}}
\DoxyCodeLine{\}\ CANInstance;}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{keyword}{typedef}\ \textcolor{keyword}{struct\ }}
\DoxyCodeLine{\{}
\DoxyCodeLine{\ \ \ \ CAN\_HandleTypeDef*\ can\_handle;}
\DoxyCodeLine{\ \ \ \ uint32\_t\ tx\_id;}
\DoxyCodeLine{\ \ \ \ uint32\_t\ rx\_id;}
\DoxyCodeLine{\ \ \ \ void\ (*can\_module\_callback)(can\_instance*);}
\DoxyCodeLine{\ \ \ \ \textcolor{keywordtype}{void}*\ id;}
\DoxyCodeLine{\}\ can\_instance\_config;}
\DoxyCodeLine{}
\DoxyCodeLine{\textcolor{keyword}{typedef}\ void\ (*can\_callback)(can\_instance*);}

\end{DoxyCode}



\begin{DoxyItemize}
\item {\ttfamily MX\+\_\+\+REGISTER\+\_\+\+DEVICE\+\_\+\+CNT}是最大的\+CAN设备注册数量，当每个设备的发送频率都较高时，设备过多会产生总线拥塞从而出现丢包和数据错误的情况。
\item {\ttfamily MX\+\_\+\+CAN\+\_\+\+FILTER\+\_\+\+CNT}是最大的\+CAN接收过滤器数量，两个\+CAN共享标号0\texorpdfstring{$\sim$}{\string~}27共28个过滤器。这部分内容比较繁杂，暂时不用理解，有兴趣自行参考\+MCU的数据手册。当前为简单起见，每个过滤器只设置一组规则用于控制一个id的过滤。
\item {\ttfamily DEVICE\+\_\+\+CAN\+\_\+\+CNT}是\+MCU拥有的\+CAN硬件数量。
\item {\ttfamily can\+\_\+instance}是一个\+CAN实例。注意，\+CAN作为一个总线设备，一条总线上可以挂载多个设备，因此多个设备可以共享同一个\+CAN硬件。其成员变量包括发送id，发送邮箱（不需要管，只是一个32位变量，\+CAN收发器会自动设置其值），发送buff以及接收buff，还有接收id和接收协议解析回调函数。\texorpdfstring{$\ast$}{*}\texorpdfstring{$\ast$}{*}由于目前使用的设备每个数据帧的长度都是8，因此收发buff长度暂时固定为8\texorpdfstring{$\ast$}{*}\texorpdfstring{$\ast$}{*}。定义该结构体的时候使用了一个技巧，使得在结构体内部可以用结构体自身的指针作为成员，即{\ttfamily can\+\_\+module\+\_\+callback}的定义。
\item {\ttfamily can\+\_\+instance\+\_\+config}是用于初始化\+CAN实例的结构，在调用\+CAN实例的初始化函数时传入（下面介绍函数时详细介绍）。
\item {\ttfamily can\+\_\+module\+\_\+callback()}是模块提供给\+CAN接收中断回调函数使用的协议解析函数指针。对于每个需要\+CAN的模块，需要定义一个这样的函数用于解包数据。
\item 每个使用\+CAN外设的module，都需要在其内部定义一个{\ttfamily can\+\_\+instance\texorpdfstring{$\ast$}{*}}。
\end{DoxyItemize}\hypertarget{md_bsp_2can_2bsp__can_autotoc_md5}{}\doxysubsection{\texorpdfstring{外部接口}{外部接口}}\label{md_bsp_2can_2bsp__can_autotoc_md5}

\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{keywordtype}{void}\ CANRegister(can\_instance*\ instance,\ can\_instance\_config\ config);}
\DoxyCodeLine{\textcolor{keywordtype}{void}\ CANSetDLC(CANInstance\ *\_instance,\ uint8\_t\ length);\ \textcolor{comment}{//\ 设置发送帧的数据长度}}
\DoxyCodeLine{uint8\_t\ CANTransmit(can\_instance*\ \_instance,\ uint8\_t\ timeout);}

\end{DoxyCode}


{\ttfamily CANRegister}是用于初始化\+CAN实例的接口，module层的模块对象（也应当为一个结构体）内要包含一个{\ttfamily usart\+\_\+instance}。调用时传入实例指针，以及用于初始化的config。{\ttfamily CANRegister}应当在module的初始化函数内被调用，推荐config采用以下的方式定义，更加直观明了：


\begin{DoxyCode}{0}
\DoxyCodeLine{can\_instance\_config\ config=\{.can\_handle=\&hcan1,}
\DoxyCodeLine{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ .tx\_id=0x005,}
\DoxyCodeLine{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ .rx\_id=0x200,}
\DoxyCodeLine{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ can\_module\_callback=MotorCallback\}}

\end{DoxyCode}


{\ttfamily CANTransmit()}是通过模块通过其拥有的\+CAN实例发送数据的接口，调用时传入对应的instance。在发送之前，应当给instance内的{\ttfamily send\+\_\+buff}赋值。\hypertarget{md_bsp_2can_2bsp__can_autotoc_md6}{}\doxysubsection{\texorpdfstring{私有函数和变量}{私有函数和变量}}\label{md_bsp_2can_2bsp__can_autotoc_md6}
在.\+c文件内设为static的函数和变量


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{keyword}{static}\ can\_instance\ *instance[MX\_REGISTER\_DEVICE\_CNT]=\{NULL\};}

\end{DoxyCode}


这是bsp层管理所有\+CAN实例的入口。


\begin{DoxyCode}{0}
\DoxyCodeLine{\textcolor{keyword}{static}\ \textcolor{keywordtype}{void}\ CANServiceInit()}
\DoxyCodeLine{static\ \textcolor{keywordtype}{void}\ CANAddFilter(can\_instance\ *\_instance)}
\DoxyCodeLine{static\ \textcolor{keywordtype}{void}\ CANFIFOxCallback(CAN\_HandleTypeDef\ *\_hcan,\ uint32\_t\ fifox)}
\DoxyCodeLine{\textcolor{keywordtype}{void}\ HAL\_CAN\_RxFifo0MsgPendingCallback(CAN\_HandleTypeDef\ *hcan)}
\DoxyCodeLine{\textcolor{keywordtype}{void}\ HAL\_CAN\_RxFifo1MsgPendingCallback(CAN\_HandleTypeDef\ *hcan)}

\end{DoxyCode}



\begin{DoxyItemize}
\item {\ttfamily CANService\+Init()}会被{\ttfamily CANRegister()}调用，对\+CAN外设进行硬件初始化并开启接收中断和消息提醒。
\item {\ttfamily CANAdd\+Filter()}在每次使用{\ttfamily CANRegister()}的时候被调用，用于给当前注册的实例添加过滤器规则并设定处理对应{\ttfamily rx\+\_\+id}的接收\+FIFO。过滤器的作用是减小\+CAN收发器的压力，只接收符合过滤器规则的报文（否则不会产生接收中断）。
\item {\ttfamily HAL\+\_\+\+CAN\+\_\+\+Rx\+Fifo0\+Msg\+Pending\+Callback()}和{\ttfamily HAL\+\_\+\+CAN\+\_\+\+Rx\+Fifo1\+Msg\+Pending\+Callback()}都是对\+HAL的\+CAN回调函数的重定义（原本的callback是{\ttfamily \+\_\+\+\_\+week}修饰的弱定义），当发生\+FIFO0或\+FIFO1有新消息到达的时候，对应的callback会被调用。{\ttfamily CANFIFOx\+Callback()}随后被前两者调用，并根据接收id和硬件中断来源（哪一个\+CAN硬件，\+CAN1还是\+CAN2）调用对应的instance的回调函数进行协议解析。
\item 当有一个模块注册了多个can实例时，通过{\ttfamily CANInstance.\+id},使用强制类型转换将其转换成对应模块的实例指针，就可以对不同的模块实例进行回调处理了。
\end{DoxyItemize}\hypertarget{md_bsp_2can_2bsp__can_autotoc_md7}{}\doxysubsection{\texorpdfstring{注意事项}{注意事项}}\label{md_bsp_2can_2bsp__can_autotoc_md7}
由于\+CAN总线自带发送检测，如果总线上没有挂载目标设备（接收id和发送报文相同的设备），那么\+CAN邮箱会被占满而无法发送。在{\ttfamily CANTransmit()}中会对\+CAN邮箱是否已满进行{\ttfamily while(1)}检查。当超出{\ttfamily timeout}之后函数会返回零，说明发送失败。

由于卡在{\ttfamily while(1)}处不断检查邮箱是否空闲，调用{\ttfamily CANTransmit()}的任务可能无法按时挂起，导致任务定时不精确。建议在没有连接\+CAN进行调试时，按需注释掉有关\+CAN发送的代码部分，或设定一个较小的{\ttfamily timeout}值，防止对其他需要精确定时的任务产生影响。 